package diff.viewer;

import java.awt.*;
import java.awt.geom.Rectangle2D;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;

/**
 * @author WL
 */
public class LineNumbersBar extends JPanel
{
    private DiffEditor editor;
    private DiffPaneManager manager;
    
    private static final int LINES_BORDER_WIDTH = 4;
    private static final String FONT_NAME = "monospaced";
    private static final int FONT_STYLE = Font.PLAIN;
    
    private static final Color LINE_COLOR = Color.GRAY;
    private static final Color NUMBER_COLOR = Color.BLACK;
    
    private static final int MIN_LINES_WIDTH = 10;
    
    private int linesWidth = MIN_LINES_WIDTH;
    private int oldLinesWidth;
    private Font lineFont;
    
    public LineNumbersBar(final DiffEditor editor)
    {
        super();
        this.editor = editor;
        
        lineFont = new Font(FONT_NAME, FONT_STYLE, editor.getFont().getSize());
        
        SwingUtilities.invokeLater(new Runnable() {
           public void run()
           {
                editor.getDocument().addDocumentListener(new DocumentListener(){
                    @Override
                    public void insertUpdate(DocumentEvent e) {
                        LineNumbersBar.this.refresh();
                    }
                    @Override
                    public void removeUpdate(DocumentEvent e) {
                        LineNumbersBar.this.refresh();
                    }
                    @Override
                    public void changedUpdate(DocumentEvent e) {
                        LineNumbersBar.this.refresh();
                    }
                });               
           }
        });
    }
    
    public void refresh()
    {
        this.revalidate();
        this.repaint();
    }
    
    public void setManager(DiffPaneManager manager)
    {
        this.manager = manager;
    }
    
    public Dimension getPreferredSize() 
    {
        return new Dimension(getBarWidth(), Integer.MAX_VALUE >> 2);
    }

    private int getBarWidth() 
    {
        return linesWidth;
    }
    
    private int linesCount()
    {
        Document doc = editor.getDocument();
        Element rootElement = doc.getDefaultRootElement();
        return rootElement.getElementCount();
    }
    
    private boolean checkLinesWidth(Graphics g) 
    {
        FontMetrics fm = g.getFontMetrics(g.getFont());
        Rectangle2D rect = fm.getStringBounds(Integer.toString(linesCount()), g);
        linesWidth = (int) rect.getWidth() + LINES_BORDER_WIDTH * 2;
        if (linesWidth != oldLinesWidth) {
            oldLinesWidth = linesWidth;
            revalidate();
            repaint();
            return true;
        }
        return false;
    }
    
    private int getNumberCount(int n) {
        return n == 0 ? 0 : (int)Math.log10(n) + 1;
    }
    
    private String formatLineNumber(int lineNumber) {
        int maxNumberCount = getNumberCount(linesCount());
        return String.format("%" + String.valueOf(maxNumberCount) + "d", lineNumber);
    }
    
    protected void paintChildren(Graphics g)
    {
        //overriden to prevent the scroll button from appearing
    }
    
    protected void paintComponent(Graphics gr) {
        final Graphics2D g = (Graphics2D) gr;
        final Rectangle clip = g.getClipBounds();
        
        if (editor == null || checkLinesWidth(gr))
            return;
        
        if (manager == null)
            return;
        
        //paint the background
        g.setColor(getBackground());
        g.fillRect(clip.x, clip.y, clip.width, clip.height);
        
        int scrollOffset = 0; //editor.isLeft() ? ((JViewport)editor.getParent()).getViewPosition().y : 0;
        
        //paint the diff lines
        DecoratedDifference [] diffs = manager.getDecorations();
        if (diffs != null)
        {
            g.setColor(LINE_COLOR);
            
            for (DecoratedDifference dd : diffs) {
                int top = editor.isLeft() ? dd.getTopLeft() : dd.getTopRight();
                int bottom = editor.isLeft() ? dd.getBottomLeft() : dd.getBottomRight();

                top -= scrollOffset;
                bottom -= scrollOffset;
                
                g.drawLine(0, top, clip.width, top);

                if (bottom != -1)
                    g.drawLine(0, bottom, clip.width, bottom);
            }
        }
        
        //paint the line numbers
        g.setColor(NUMBER_COLOR);
        Font oldFont = g.getFont();
        g.setFont(lineFont);
        
        try {
            for (int i = 0; i < linesCount(); i++) 
            {
                int startOffset = editor.getDocument().getDefaultRootElement().getElement(i).getStartOffset();
                Rectangle rec1 = editor.modelToView(startOffset);

                int yOffset = rec1.y + rec1.height - rec1.height / 4;
                g.drawString(formatLineNumber(i+1), LINES_BORDER_WIDTH, yOffset - scrollOffset);
            }
        } catch (BadLocationException exp) {
            //can be ignored
        }
        
        g.setFont(oldFont);
    }
}
